﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Tao.OpenGl;
using System.IO;

namespace CutAways.Shaders
{
    class ShaderHolder
    {
        protected List<int> vertexShaderId;
        protected List<int> fragmentShaderId;

        protected int programShaderId;
        protected string programShaderInfo;

        public ShaderHolder()
        {
            vertexShaderId = new List<int>();
            fragmentShaderId = new List<int>();

            programShaderId = -1;
            programShaderInfo = "";
        }

        public int Program
        {
            get { return programShaderId; }
        }
        
        public void LoadShaders(string[] vertexShaderFile, string[] fragmentShaderFile, string info)
        {
            foreach (string vs in vertexShaderFile)
                AddVertexShader(vs);

            foreach (string fs in fragmentShaderFile)
                AddFragmentShader(fs);

            AddProgramShader();

            programShaderInfo = info;
        }

        private string GetString(string filename)
        {
            bool exist1;
            exist1 = File.Exists(filename);
            if (!exist1)
            {
                System.Windows.Forms.MessageBox.Show("file " + filename + " does not exist");
                return null;
            }

            StreamReader reader;
            reader = File.OpenText(filename);
            string shaderString = reader.ReadToEnd();
            reader.Close();

            return shaderString;
        }

        private int AddVertexShader(string filename)
        {
            string vertexShaderString = GetString(filename);
            int vId = CreateVertexShader(vertexShaderString);
            if (!CheckShader(vId))
                return -1;

            vertexShaderId.Add(vId);
            return vId;
        }

        private int AddFragmentShader(string filename)
        {
            string fragmentShaderString = GetString(filename);
            int fId = CreateFragmentShader(fragmentShaderString);
            if (!CheckShader(fId))
                return -1;

            fragmentShaderId.Add(fId);
            return fId;
        }

        private int AddProgramShader()
        {
            int programId;
            programId = Gl.glCreateProgram();

            foreach (int shaderId in vertexShaderId)
                Gl.glAttachShader(programId, shaderId);
            foreach (int shaderId in fragmentShaderId)
                Gl.glAttachShader(programId, shaderId);

            Gl.glLinkProgram(programId);
            if (!CheckProgram(programId))
                return -1;

            programShaderId = programId;
            return programId;
        }

        private int CreateVertexShader(string vertexShaderString)
        {
            int shaderId = Gl.glCreateShader(Gl.GL_VERTEX_SHADER);
            string[] text = { vertexShaderString };
            Gl.glShaderSource(shaderId, 1, text, null);
            Gl.glCompileShader(shaderId);

            return shaderId;
        }

        private int CreateFragmentShader(string fragmentShaderString)
        {
            int shaderId = Gl.glCreateShader(Gl.GL_FRAGMENT_SHADER);
            string[] text = { fragmentShaderString };
            Gl.glShaderSource(shaderId, 1, text, null);
            Gl.glCompileShader(shaderId);

            return shaderId;
        }

        private bool CheckShader(int shaderId)
        { 
            int res = -1;
            Gl.glGetShaderiv(shaderId, Gl.GL_COMPILE_STATUS, out res);
            if (res != Gl.GL_TRUE)
            { 
                int logLength;
                Gl.glGetShaderiv(shaderId, Gl.GL_INFO_LOG_LENGTH, out logLength);

                StringBuilder infoLog = new StringBuilder();
                int length;
                Gl.glGetShaderInfoLog(shaderId, logLength, out length, infoLog);
                string str = infoLog.ToString();
                System.Windows.Forms.MessageBox.Show(str);
                return false;
            }

            return true;
        }

        private bool CheckProgram(int programId)
        {
            int res = -1;
            Gl.glGetProgramiv(programId, Gl.GL_LINK_STATUS, out res);
            if (res != Gl.GL_TRUE)
            {
                int logLength;
                Gl.glGetProgramiv(programId, Gl.GL_INFO_LOG_LENGTH, out logLength);

                StringBuilder infoLog = new StringBuilder();
                int length;
                Gl.glGetProgramInfoLog(programId, logLength, out length, infoLog);
                string str = infoLog.ToString();
                System.Windows.Forms.MessageBox.Show(str);
                return false;
            }

            return true;
        }
    }
}
